package org.zjvis.datascience.web.shiro.config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SubjectFactory;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.zjvis.datascience.web.filter.JwtFilter;
import org.zjvis.datascience.web.shiro.JwtDefaultSubjectFactory;
import org.zjvis.datascience.web.shiro.JwtRealm;
import org.zjvis.datascience.web.shiro.Realm;

/**
 * @Description : Shiro 全局配置类
 * @Date 2020-06-01
 */
@Configuration
public class ShiroConfig {

    private static final String COOKIE_NAME = "rememberMe";
    @Value("${shiro.cipher-key}")
    private String base64CipherKey;

    @Autowired
    private JwtRealm jwtRealm;

    @Autowired
    private Realm realm;

    /**
     * a. 告诉shiro不要使用默认的DefaultSubject创建对象，因为不能创建Session
     **/
    @Bean
    public SubjectFactory subjectFactory() {
        return new JwtDefaultSubjectFactory();
    }

    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        List reamls = new ArrayList();
        reamls.add(jwtRealm);
        reamls.add(realm);
        securityManager.setRealms(reamls);
        securityManager.setRememberMeManager(rememberMeManager());

        // 关闭 ShiroDAO 功能
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        // 不需要将 Shiro Session 中的东西存到任何地方（包括 Http Session 中）
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        //禁止Subject的getSession方法
        securityManager.setSubjectFactory(subjectFactory());
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager());
        shiroFilter.setLoginUrl("/unauthenticated");
        shiroFilter.setUnauthorizedUrl("/unauthorized");
        Map<String, Filter> filterMap = new HashMap<String, Filter>(2);
        filterMap.put("jwt", new JwtFilter());
        filterMap.put("logout", new LogoutFilter());
        shiroFilter.setFilters(filterMap);

        // 拦截器
        Map<String, String> filterRuleMap = new LinkedHashMap<>();
        filterRuleMap.put("/auth/login", "anon");
        // note: 匿名接口取不到登录用户信息
        filterRuleMap.put("/auth/code", "anon");
        filterRuleMap.put("/auth/logout", "anon");
        filterRuleMap.put("/users/create", "anon");
        filterRuleMap.put("/users/psdModify", "anon");
        filterRuleMap.put("/dataset/httpDataImport/**", "anon");
        filterRuleMap.put("/callback/**", "anon");

        // 短信相关接口开放
        filterRuleMap.put("/sms/registerSendMsg", "anon");
        filterRuleMap.put("/sms/modifySendMsg", "anon");
        filterRuleMap.put("/sms/verify", "anon");
        filterRuleMap.put("/sms/captcha", "anon");

//        filterRuleMap.put("/auth/userRegister", "anon");
//        filterRuleMap.put("/auth/resetPassword", "anon");
//        filterRuleMap.put("/auth/getCodeBySentEmail", "anon");
//        filterRuleMap.put("/users/**", "jwt");
        filterRuleMap.put("/apollo/*", "anon");
        //jobserver转发接口开放
        filterRuleMap.put("/job/**", "anon");

        //可视化构建发布
        filterRuleMap.put("/publish/**", "anon");

        //swagger接口权限 开放
        filterRuleMap.put("/swagger-ui.html", "anon");
        filterRuleMap.put("/webjars/**", "anon");
        filterRuleMap.put("/v2/**", "anon");
        filterRuleMap.put("/swagger-resources/**", "anon");
        filterRuleMap.put("/miserver/*", "anon");
        filterRuleMap.put("/**", "jwt");

        //filterRuleMap.put("/**", "anon");
        shiroFilter.setFilterChainDefinitionMap(filterRuleMap);

        return shiroFilter;
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启aop注解支持
     *
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 初始化Authenticator验证管理器，如不注入，则会导致验证失败返回未登录
     * Authorizer授权器：赋予主体有哪些权限
     */
    @Bean
    public Authenticator authenticator() {
        //扩展父类原方法，捕获原始异常
        ModularRealmAuthenticator authenticator = new ShiroModularRealmAuthenticator();
        //设置两个Realm，一个用于用户登录验证和访问权限获取；一个用于jwt token的认证
        authenticator.setRealms(Arrays.asList(jwtRealm, realm));
        /**
         FirstSuccessfulStrategy：只要有一个 Realm 验证成功即可，只返回第一个 Realm 身份验证成功的认
         AtLeastOneSuccessfulStrategy：只要有一个 Realm 验证成功即可，和 FirstSuccessfulStrategy 不
         AllSuccessfulStrategy：所有 Realm 验证成功才算成功，且返回所有 Realm 身份验证成功的认证信息
         */
        //设置多个realm认证策略，一个成功即跳过其它的
        authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return authenticator;
    }

    @Bean
    public RememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        //注入自定义cookie(主要是设置寿命, 默认的一年太长)
        SimpleCookie simpleCookie = new SimpleCookie(COOKIE_NAME);
        simpleCookie.setHttpOnly(true);
        //设置RememberMe的cookie有效期为7天
        simpleCookie.setMaxAge(604800);
        rememberMeManager.setCookie(simpleCookie);
        rememberMeManager.setCipherService(new AesCipherService());

        byte[] cipherKey = Base64.getDecoder().decode(base64CipherKey);
        rememberMeManager.setCipherKey(cipherKey);
        return rememberMeManager;
    }

}
